﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using log4net;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.QueryParsers;
using Lucene.Net.Search;
using Lucene.Net.Store;
using SnapUtilities;

namespace LuceneSearcher
{
    public class SimpleLuceneSearcher : ISearcher
    {
        private static readonly ILog Logger =
            LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private readonly Stopwatch stopwatch = new Stopwatch();
        private const int MaxNumberSearchTerms = 500;

        private readonly Directory directory;

        public  SimpleLuceneSearcher(Directory directory)
        {
            this.directory = directory;
        }

        public IEnumerable<SearchResult> SearchNewsItems(DateTime splitDate)
        {
            Logger.DebugFormat( "Simple-Lucene Searching with news items older than {0}...", splitDate );
            var results = new List<SearchResult>();
            var indexSearcher = new IndexSearcher( directory, true );
            var parser = new QueryParser( Lucene.Net.Util.Version.LUCENE_29,
                                          "body",
                                          new StandardAnalyzer( Lucene.Net.Util.Version.LUCENE_29 ) );
            var newsInfos = Utilities.PullSingleCompanyNewsInfo( splitDate, DateTime.MaxValue );

            stopwatch.Restart();
            foreach (var newsId in newsInfos.Keys)
            {
                var searchResult = new SearchResult();
                searchResult.NewsId = newsId;
                var searchStrings = PrepareSearchStrings( newsInfos[newsId] );
                foreach (var searchString in searchStrings)
                {
                    if (String.IsNullOrEmpty( searchString )) continue;
                    var query = parser.Parse( searchString );
                    var hits = indexSearcher.Search( query, 1 );
                    if (hits.scoreDocs.Length == 0) continue;

                    var document = indexSearcher.Doc( hits.scoreDocs[0].doc );
                    searchResult.BestMatchNewsId = Int32.Parse( document.Get( "id" ) );
                    searchResult.BestMatchScore = hits.scoreDocs[0].score;
                    results.Add( searchResult );
                }
            }
            stopwatch.Stop();
            Logger.DebugFormat( "Found {0} result sets in {1}", results.Count, stopwatch.Elapsed );

            return results;//.Distinct();
        }

        private static IEnumerable<string> PrepareSearchStrings( string newsInfo )
        {
            var cleanedText = CleanStringForParsing( newsInfo );
            var words = cleanedText.Split( " ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries );
            var paragraphs = new List<string>();
            for (int i = 0; i <= words.Length / MaxNumberSearchTerms; i++)
            {
                var builder = new StringBuilder();
                foreach (var word in words.ToList().Skip(MaxNumberSearchTerms * i).Take(MaxNumberSearchTerms))
                {
                    builder.AppendFormat( "{0} ", word );
                }
                paragraphs.Add( builder.ToString() );
            }

            return paragraphs;
        }

        private static string CleanStringForParsing(string text)
        {
            // Remove metasearch symbols that Lucene may misconstrue
            //  as search operators.
            text = text.Replace( "(", String.Empty )
                .Replace( ")", String.Empty )
                .Replace( "-", String.Empty )
                .Replace( "+", String.Empty )
                .Replace( "*", String.Empty )
                .Replace( "?", String.Empty )
                .Replace( "]", String.Empty )
                .Replace( "[", String.Empty )
                .Replace( "{", String.Empty )
                .Replace( "}", String.Empty )
                .Replace( "<", String.Empty )
                .Replace( ">", String.Empty )
                .Replace( "“", "" )
                .Replace( "”", "" );

            // Remove newline and linefeed symbols
            text = text.Replace( Environment.NewLine, " " );
            text = text.Replace( "\n", " " );
            text = text.Replace( "\r", " " );
            
            if (String.IsNullOrEmpty(text)) return String.Empty;
            if (!Regex.IsMatch(text, "[a-z]+", RegexOptions.IgnoreCase)) return String.Empty;

            return QueryParser.Escape( text );
        }
    }
}
